package calcops

import "strconv"
import "math"

type op func (a,b float64) float64

var ops = map[rune]op {
	'+' : func (a,b float64) float64 { return a+b },
	'-' : func (a,b float64) float64 { return a-b },
	'*' : func (a,b float64) float64 { return a*b },
	'/' : func (a,b float64) float64 { return a/b },
	'^' : func (a,b float64) float64 { return math.Pow (a,b) },
}

func isWhite (input rune) bool {
	white := [4]rune {
		' ',
		'\t',
		'\n',
		'\r', // for Winblows
	}
	
	for _,char := range white {
		if char == input {
			return true;
		}
	}
	return false;
}

func stripWhite (input string) string {
	output := ""
	
	for _,char := range input {
		if !isWhite (char) { output += string (char) }
	}
	return output
}

// Evaluates the result of an arithmetic operation and returns the result
// and an error. The error will be <nil> if the input string doesn't make
// sense to execute arithmetically.
func Evaluate (input string) (float64, error) {
	var opIndex int // index position of the operator in the input string
	var opFunct op // the function corresponding to the operator
	
	input = stripWhite (input) // strip whitespace
	
	for i,char := range input {
		// if the char corresponds to an operation...
		if fn,ok := ops[char]; ok {
			opFunct = fn // fetch the corresponding op function
			opIndex = i
			break
		}
	}
	
	a,errA := strconv.ParseFloat (input[0:opIndex], 64)
	if errA != nil { return 0, errA }
	b,errB := strconv.ParseFloat (input[opIndex+1 : len(input)], 64)
	if errB != nil { return 0, errB }
	return opFunct (a, b), nil
}
